package socket

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/kinwyb/appTools/common"
	"github.com/sirupsen/logrus"
	"io"
	"net"
	"sync"
)

// TcpServerConfig tcp服务端配置
type TcpServerConfig struct {
	Port               int64                                                  //监听端口
	ServerAddress      string                                                 //服务监听地址
	Log                *logrus.Entry                                          //日志
	ErrorHandler       func(errType ErrorType, err error, clientID ...string) //错误处理
	NewClientHandler   func(clientID string) TcpProtocol                      //新客户端连接回调,返回该客户端处理协议,可以返回nil
	MessageHandler     func(clientID string, msg []byte)                      //消息处理
	CloseHandler       func()                                                 //服务关闭回调
	ClientCloseHandler func(clientID string)                                  //客户端关闭回调
}

// TcpServer TCP服务器对象
type TcpServer struct {
	ctx      context.Context
	config   *TcpServerConfig
	conn     net.Listener
	clients  *sync.Map
	readData chan []byte //读取到的数据
	doClose  bool        //关闭操作
}

// NewTcpServer 新建一个服务端
func NewTcpServer(ctx context.Context, config *TcpServerConfig) (*TcpServer, error) {
	if config == nil {
		return nil, errors.New("配置信息不能为空")
	}
	ret := &TcpServer{
		ctx:     ctx,
		config:  config,
		clients: &sync.Map{},
	}
	return ret, nil
}

// Listen 启动监听该方法会
func (s *TcpServer) Listen() {
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.config.ServerAddress, s.config.Port))
	if err != nil {
		if s.config.Log != nil {
			s.config.Log.Infof("查询Socket监听失败:%s", err.Error())
		}
		if s.config.ErrorHandler != nil {
			s.config.ErrorHandler(ListenErr, err)
		}
		return
	}
	s.Start(listener)
}

func (s *TcpServer) Start(l net.Listener) error {
	if l == nil {
		return errors.New("listener is nil")
	}
	s.conn = l
	s.doClose = false
	if s.config.Log != nil {
		s.config.Log.Infof("服务器监听开启 => %s:%d", s.config.ServerAddress, s.config.Port)
	}
	go s.handleConn()
	<-s.ctx.Done()
	s.Close()
	return nil
}

// Close 关闭服务
func (s *TcpServer) Close() {
	s.doClose = true
	if s.conn != nil {
		_ = s.conn.Close()
		s.conn = nil
	}
	if s.config.CloseHandler != nil {
		s.config.CloseHandler()
	}
	s.clients.Range(func(key, value interface{}) bool {
		if sclient, ok := value.(*serverClient); ok {
			sclient.Close()
		}
		return true
	})
}

// 处理新连接
func (s *TcpServer) handleConn() {
	defer recoverPainc(s.config.Log, s.handleConn)
	for {
		if conn, err := s.conn.Accept(); err != nil {
			if s.doClose { //关闭
				return
			}
			if s.config.Log != nil {
				s.config.Log.Errorf("监听请求连接失败:%s", err.Error())
			}
			if s.config.ErrorHandler != nil {
				s.config.ErrorHandler(ListenErr, err)
			}
			s.Close()
			return
		} else {
			s.newClientAccept(conn)
		}
	}
}

// 发送数据
func (s *TcpServer) Write(clientID string, msg []byte) error {
	if s.conn == nil {
		return errors.New("连接已关闭")
	}
	client, ok := s.clients.Load(clientID)
	if client == nil || !ok {
		return errors.New("客户端链接不存在")
	}
	if sclient, ok2 := client.(*serverClient); ok2 {
		return sclient.write(msg)
	} else {
		return errors.New("客户端链接无效")
	}
}

// 读取消息
func (s *TcpServer) messageRecv(clientID string, msg []byte) {
	if s.config.MessageHandler != nil {
		s.config.MessageHandler(clientID, msg)
	}
}

// CloseClient 关闭指定客户端
func (s *TcpServer) CloseClient(clientID string) {
	client, ok := s.clients.Load(clientID)
	if !ok {
		return
	}
	if client != nil {
		if c, ok := client.(*serverClient); ok {
			c.Close()
		}
	}
	s.clients.Delete(clientID)
}

func (s *TcpServer) newClientAccept(conn net.Conn) {
	sclient := &serverClient{
		conn:   conn,
		server: s,
		ID:     clientIDGen(),
	}
	var protocol TcpProtocol
	if s.config.NewClientHandler != nil {
		protocol = s.config.NewClientHandler(sclient.ID)
	}
	sclient.protocol = protocol
	sclient.doClose = false
	go sclient.readData()
	if protocol != nil {
		go sclient.recvProtocolMsg()
	}
	s.clients.Store(sclient.ID, sclient)
}

// GetAllConnIDs 获取所有有效链接
func (s *TcpServer) GetAllConnIDs() []string {
	var ret []string
	s.clients.Range(func(key, value interface{}) bool {
		ret = append(ret, key.(string))
		return true
	})
	return ret
}

// 客户端id生成器
func clientIDGen() string {
	return common.IDGen()
}

// TCP连接到服务器的客户端
type serverClient struct {
	conn     net.Conn
	server   *TcpServer
	protocol TcpProtocol
	ID       string //客户端唯一标示
	doClose  bool   //关闭
}

// 读取客户端数据
func (s *serverClient) readData() {
	defer s.Close()
	data := make([]byte, 1024)
	for {
		if s.conn == nil { //应用已经关闭
			return
		}
		i, err := s.conn.Read(data)
		if s.doClose {
			return
		} else if err != nil {
			if err == io.EOF { //读取结束
				return
			}
			if s.server.config.Log != nil {
				s.server.config.Log.Errorf("%s=>数据读取错误:%s", s.ID, err.Error())
			}
			if s.server.config.ErrorHandler != nil {
				s.server.config.ErrorHandler(ReadErr, err, s.ID)
			}
			return
		}
		if s.protocol != nil {
			s.protocol.UnPacking(data[0:i])
		} else {
			s.server.messageRecv(s.ID, data[0:i])
		}
	}
}

// 获取通讯协议解析出来的消息
func (s *serverClient) recvProtocolMsg() {
	defer recoverPainc(s.server.config.Log, s.recvProtocolMsg)
	if s.protocol != nil {
		for {
			msg := <-s.protocol.ReadMsg()
			s.server.messageRecv(s.ID, msg)
		}
	}
}

// 发送数据
func (s *serverClient) write(msg []byte) error {
	if s.conn == nil {
		return errors.New("连接已关闭")
	}
	if s.protocol != nil {
		msg = s.protocol.Packing(msg)
	}
	_, err := s.conn.Write(msg)
	return err
}

// Close 关闭连接
func (s *serverClient) Close() {
	if s.doClose {
		return
	}
	s.doClose = true
	s.server.clients.Delete(s.ID)
	if s.conn != nil {
		e := s.conn.Close()
		if e != nil && s.server.config.Log != nil {
			s.server.config.Log.WithError(e).Error("关闭tcp client链接失败")
		}
		s.conn = nil
		if s.server.config.Log != nil {
			s.server.config.Log.WithField("info", s.ID).Error("关闭tcp client链接")
		}
		if s.server.config.ClientCloseHandler != nil {
			s.server.config.ClientCloseHandler(s.ID)
		}
	}
}
